package com.sskj.base.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Handler;
import android.os.Vibrator;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.hjq.toast.ToastUtils;
import com.sskj.base.R;

import java.util.ArrayList;
import java.util.List;

public class GestureLockView extends View {

    private Paint circlePaint;

    private Paint selectPaint;

    private int selectColor = Color.BLUE;

    private int defaultColor = Color.GRAY;

    private int errColor = Color.RED;

    //大圆半径
    private int circleRadius = 10;
    //小圆半径
    private int smallRadius;

    private Path linePath;

    private Path temPath;

    private int width;

    private int height;

    private List<Circle> circles = new ArrayList<>();

    private boolean first = true;

    private float stockWidth = 5;

    private int rootX;

    private int rootY;

    //默认密码
    private String defaultPassword = "6458";
    //选择的密码
    private String password = "";

    private List<Integer> selectPoint = new ArrayList<>();

    private int tryTimes = 0;

    private int maxTryTimes = 5;

    private long sleepTime = 10000;

    //是否超过最大尝试次数
    private boolean isMax = false;

    /**
     * 是否可以画线
     * 当选中第一个点后才可以画线否则不画线
     */
    private boolean isLine;
    /**
     * 是否是第一次选中
     * 当第一次选中时移动线的起点到选中的点
     */
    private boolean isFirst = true;
    //是否可触摸
    private boolean touchAble = true;
    //震动
    Vibrator vibrator;
    //成功失败监听
    private GestureListener listener;

    public GestureLockView(Context context) {
        super(context);
        init(null);
    }

    public GestureLockView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public GestureLockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        if (attrs != null) {
            TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.GestureLockView);
            defaultColor = typedArray.getColor(R.styleable.GestureLockView_defaultColor, defaultColor);
            selectColor = typedArray.getColor(R.styleable.GestureLockView_selectColor, selectColor);
            errColor = typedArray.getColor(R.styleable.GestureLockView_errColor, errColor);
            typedArray.recycle();
        }
        vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);

        circlePaint = new Paint();
        circlePaint.setColor(defaultColor);
        circlePaint.setAntiAlias(true);

        selectPaint = new Paint();
        selectPaint.setColor(selectColor);
        selectPaint.setAntiAlias(true);
        selectPaint.setStrokeWidth(stockWidth);
        selectPaint.setStyle(Paint.Style.STROKE);

        linePath = new Path();
        temPath = new Path();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth();
        height = getMeasuredHeight();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (first) {
            width = width - getPaddingLeft() - getPaddingRight();
            height = height - getPaddingTop() - getPaddingBottom();
            int x = width / 6;
            int y = height / 6;
            circleRadius = x / 3;
            smallRadius = x / 8;
            int id = 0;
            for (int j = 0; j < 3; j++) {
                for (int i = 0; i < 3; i++) {
                    int temX = (i * 2 + 1) * x + getPaddingLeft();
                    int temY = (j * 2 + 1) * y + getPaddingTop();
                    Circle circle = new Circle(temX, temY, id);
                    circles.add(circle);
                    id++;
                }
            }
            first = false;
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (Circle c : circles) {
            switch (c.getState()) {
                case Circle.State.NORMAL:
                    circlePaint.setColor(defaultColor);
                    canvas.drawCircle(c.getX(), c.getY(), smallRadius, circlePaint);
                    break;
                case Circle.State.SELECT:
                    circlePaint.setColor(selectColor);
                    selectPaint.setColor(selectColor);
                    canvas.drawCircle(c.getX(), c.getY(), smallRadius, circlePaint);
                    canvas.drawCircle(c.getX(), c.getY(), circleRadius, selectPaint);
                    break;
                case Circle.State.ERROR:
                    circlePaint.setColor(errColor);
                    selectPaint.setColor(errColor);
                    canvas.drawCircle(c.getX(), c.getY(), smallRadius, circlePaint);
                    canvas.drawCircle(c.getX(), c.getY(), circleRadius, selectPaint);
                    break;

            }
        }

        canvas.drawPath(linePath, selectPaint);

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!touchAble || isMax) {
            return true;
        }
        float tx = event.getX();
        float ty = event.getY();
        Circle circle = getTouchCircle(tx, ty);
        if (circle != null) {
            isLine = true;
            circle.setState(Circle.State.SELECT);
            //添加选中的点并震动提示
            if (!password.contains(circle.getId() + "")) {
                selectPoint.add(circle.getId());
                password = password + circle.getId();
                vibrator.vibrate(50);
            }
            rootX = circle.getX();
            rootY = circle.getY();
            /**
             * 第一次选中点移动该位置
             */
            if (isFirst) {
                isFirst = false;
                temPath.moveTo(rootX, rootY);
            } else {
                temPath.lineTo(rootX, rootY);
            }

        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                if (isLine) {
                    linePath.reset();
                    linePath.addPath(temPath);
                    linePath.moveTo(rootX, rootY);
                    // 如果正在画线且在圆的范围内则不画
                    if (circle == null) {
                        linePath.lineTo(tx, ty);
                    }
                }

                break;
            case MotionEvent.ACTION_UP:
                if (password.isEmpty()) {
                    return true;
                }
                if (password.length() < 4) {
                    ToastUtils.show("至少需要4个点");
                    return true;
                }
                linePath.reset();
                linePath.addPath(temPath);
                linePath.moveTo(rootX, rootY);
                linePath.lineTo(rootX, rootY);
                if (checkPassword()) {
                    reset();
                } else {
                    setError();
                }
                break;
        }
        invalidate();
        return true;
    }

    /**
     * 验证密码是否正确
     * 超过尝试次数 等待 sleepTime
     *
     * @return
     */
    private boolean checkPassword() {
        tryTimes++;
        if (defaultPassword.equals(password)) {
            if (listener != null) {
                listener.onSuccess();
            }
            tryTimes = 0;
            sleepTime = 10000;
            isMax = false;
        } else {
            if (listener != null) {
                listener.onError();
            }
            if (tryTimes > maxTryTimes) {
                tryTimes = 0;
                isMax = true;
                sleepTime = sleepTime * 2;
                ToastUtils.show(sleepTime + "S后重试");
                new Handler().postDelayed(() -> {
                    isMax = false;
                }, sleepTime);
            }
        }
        return defaultPassword.equals(password);
    }

    /**
     * 重置状态
     */
    private void reset() {
        isLine = false;
        isFirst = true;
        temPath.reset();
        linePath.reset();
        selectPoint.clear();
        password = "";
        touchAble = true;
        for (Circle c : circles) {
            c.setState(Circle.State.NORMAL);
        }
    }

    /**
     * 获取选中的点
     * 偏移量50 更好选中
     *
     * @param x
     * @param y
     * @return
     */
    private Circle getTouchCircle(float x, float y) {
        for (Circle c : circles) {
            if (Math.pow(x - c.getX(), 2) + Math.pow(y - c.getY(), 2) <= Math.pow(circleRadius + 50, 2)) {
                return c;
            }
        }
        return null;
    }

    /**
     * 设置为错误状态
     */
    private void setError() {
        touchAble = false;
        for (int c : selectPoint) {
            circles.get(c).setState(Circle.State.ERROR);
        }
        Handler handler = new Handler();
        handler.postDelayed(() -> {
            reset();
            invalidate();
        }, 2000);
    }


    public void setTouchAble(boolean touchAble) {
        this.touchAble = touchAble;
    }

    public void setGestureListener(GestureListener listener) {
        this.listener = listener;
    }


    public interface GestureListener {
        void onSuccess();

        void onError();
    }

    class Circle {

        private int x;
        private int y;
        private int id;

        private int state = State.NORMAL;

        public Circle(int x, int y, int id) {
            this.x = x;
            this.y = y;
            this.id = id;
        }


        public int getState() {
            return state;
        }

        public void setState(int state) {
            this.state = state;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        class State {

            public static final int NORMAL = 0;
            public static final int SELECT = 1;
            public static final int ERROR = -1;
        }

    }

}


